/**
 * @file main_slave.cpp Klientská časť programu pre paralelný výpočet diskrétneho logaritmu.
 *
 * @author Martin Kosorinský.
 * @date 30.10.2009.
 */

#include "CRhoBrentPvmAlg.hpp"

using namespace std;

/**
 * @brief Na štandardný výstup (stdout) vypíše základné informácie o použití programu a jeho parametroch.
 */
void printUsage();
/**
 * @brief Vstupná funkcia PVM klienta pre výpočet ECDLP pomocou Brentovho algoritmu.
 */
int main(int argc, char* argv[]) {

    if (argc != 3)  {
        cerr << "Error: '" << __FUNCTION__ << "': Nespravny pocet argumentov!" << endl;
        printUsage();
        exit(EXIT_FAILURE);
    }

    int myTID = pvm_mytid();
    int parentTID = pvm_parent();

    if (parentTID == PvmNoParent) {
        cerr << "Error: '" << __FUNCTION__ << "': Slave nemoze by spustany samostatne (bez PVM rodica)!" << endl;
        pvm_exit();
        exit(EXIT_FAILURE);
    }
    pvm_notify(PvmTaskExit, CPvmCommands::PVM_ECDLP_EXIT, 1, &parentTID); // vyziadame si informacie o zlyhani hlavneho uzla

    tracer << "Node started and initialized (waiting for ECParams and TaskPoint)." << endl;

    // inicializacia krivky a kniznice miracl -> pred jej volanim nesmie byt
    // ziadna alokacia premennych big alebo epoint!!!
    mirsys(atoi(argv[1]), 0);

    epoint * TP = epoint_init();

    big A = mirvar(0);
    big B = mirvar(0);
    big p = mirvar(0);
    big ord = mirvar(0);
    big x = mirvar(0);
    big y = mirvar(0);

    cout << "Waiting for ECParams." << endl;
    CPvmCommands::pvmRecv(parentTID, CPvmCommands::PVM_ECDLP_ECPARAMS);
    CPvmSlaveCommands::pvmUnpackECParams(A, B, p, ord, x, y);
    CECContext::init(A, B, p, ord, x, y, MR_AFFINE);
    irand((unsigned int)time(0)*myTID);

    cout << "Waiting for TaskPoint." << endl;
    CPvmCommands::pvmRecv(parentTID, CPvmCommands::PVM_ECDLP_TASKPOINT);
    CPvmSlaveCommands::pvmUnpackTaskPoint(TP);
    CECContext::setPoint(TP);

    big vysledok = mirvar(0);

    while (true) {
        if (CRhoBrentPvmAlg::computeLogarithm(parentTID, atoi(argv[2]), vysledok)) {
            CPvmSlaveCommands::pvmSendDL(parentTID, vysledok);
            cout << "Vysledok = " << vysledok << endl;
            break;
        }
        else {
            tracer << "Restart Brentovho algoritmu." << endl;
        }

        // overime, ci master uzol neohlasil svoje ukoncenie
        if (pvm_probe(-1, CPvmCommands::PVM_ECDLP_EXIT) != 0) {
            cerr << "Error: '" << __FUNCTION__ << "': Master uzol bol preruseny, ukoncujeme vypocet!" << endl;
            exit(EXIT_FAILURE);
        }
    }

    cout << "Node stopping." << endl;

    mirkill(x);
    mirkill(y);
    mirkill(A);
    mirkill(B);
    mirkill(p);
    mirkill(ord);

    epoint_free(TP);
    mirkill(vysledok);

    pvm_exit();
    CECContext::free();

    return 0;
}

void printUsage() {
    cout << "Klientsky program pre paralelny vypocet diskretneho logaritmu." << endl;
    cout << "Program nie je mozne spustat samostatne bez PVM rodica!." << endl;
    cout << "Pouzitie: ecdlp_slave  <mirsys:int> <filter_param:int>" << endl;
    cout << "\t<mirsys:int>       :  Presnost kniznice MIRACL" << endl;
    cout << "\t<filter_param:int> :  Parameter filtrovacej funkcie (distinguished points), pocet trailing 0 v suradnici" << endl;
}
